home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
9675
/
9675.xpi
/
chrome
/
content
/
simpletimer-notify.js
< prev
next >
Wrap
Text File
|
2009-11-17
|
32KB
|
816 lines
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Simple Timer.
*
* The Initial Developer of the Original Code is
* George Bradt.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var SimpleTimerNotify = {
// For reference, not used.
notification: {completed: false,
recurring: false,
timeDisplay: "",
description: "",
time24hr: "",
timeMilli: 0,
day: -1,
url: ""
},
notifySep: "\x1D", // used to separate nots. in pref.
// Called when loading Notify Me At dialog.
// Populate the tree.
onLoadNotify: function() {
var strbundle = document.getElementById("simtim-strings");
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
var notifyTable = [];
if ( Application.storage.has("notifyTable") ) {
notifyTable = Application.storage.get("notifyTable", "ERR");
if ( notifyTable === "ERR" ) {
alert(strbundle.getString("alert.error.loading.notifications"));
return;
}
}
else {
alert(strbundle.getString("alert.error.loading.notifications"));
return;
}
var treeChildren = document.getElementById("simtim-treeItems");
// Clear the old tree data.
this.onDeleteAllNotify();
for ( var i in notifyTable ) {
// Bypass completed notifications.
if ( !notifyTable[i].completed ) {
this.addTreeRow(treeChildren, notifyTable[i]);
}
}
},
// Called when user clicks Bookmark.
onBookmarkNotify: function() {
var strbundle = document.getElementById("simtim-strings");
var url = window.opener.gBrowser.currentURI.spec;
if ( url === "about:blank" ) {
alert(strbundle.getString("alert.warning.no.url"));
}
else {
document.getElementById("simtim-tboxNotifyUrl").value = url;
}
},
// Called when user clicks Recurring checkbox.
// Enable/disable day menulist.
onClickRecurringNotify: function() {
document.getElementById("simtim-mlistDow").disabled =
!document.getElementById("simtim-cboxNotifyRecurring").checked;
},
// Called when user clicks on Notifications tree.
onClickNotTree: function(event) {
var strbundle = document.getElementById("simtim-strings");
var recurring, dow, mlistDow;
var tree = document.getElementById("simtim-treeNotifications");
// See if tree disabled (ie. edit already in progress).
if ( tree.disabled ) {
return;
}
// Get the row, col and child element at the point of mouse click
var tbo = tree.treeBoxObject;
var row = { }, col = { }, child = { };
tbo.getCellAt(event.clientX, event.clientY, row, col, child);
if ( col.value ) {
if ( col.value.id !== "simtim-colRecur" &&
event.detail === 2 ) {
// No monkey business.
tree.disabled = true;
// User double-clicked on a notification to initiate an edit.
// Disable the delete and reset buttons.
document.getElementById("simtim-btnDel").disabled = true;
document.getElementById("simtim-btnDelAll").disabled = true;
document.getElementById("simtim-btnReset").disabled = true;
// Change label and tooltips on Add and Add/OK buttons.
document.getElementById("simtim-btnAddOk").label =
strbundle.getString("button.label.replace/OK");
document.getElementById("simtim-btnAddOk").tooltipText =
strbundle.getString("button.tooltip.replace/OK");
document.getElementById("simtim-btnAdd").label =
strbundle.getString("button.label.replace");
document.getElementById("simtim-btnAdd").tooltipText =
strbundle.getString("button.tooltip.replace");
// Move the notification data back to the upper half.
document.getElementById("simtim-tboxNotifyDesc").value =
tree.view.getCellText(row.value,
tree.columns.getNamedColumn("simtim-colDescription"));
document.getElementById("simtim-tboxNotifyUrl").value =
tree.view.getCellText(row.value,
tree.columns.getNamedColumn("simtim-colUrl"));
document.getElementById("simtim-tspinNotifyTime").value =
tree.view.getCellValue(row.value,
tree.columns.getNamedColumn("simtim-colTime"));
mlistDow = document.getElementById("simtim-mlistDow");
recurring = tree.view.getCellValue(row.value,
tree.columns.getNamedColumn("simtim-colRecur"));
if ( recurring === "true" ) {
document.getElementById("simtim-cboxNotifyRecurring").checked = true;
dow = tree.view.getCellValue(row.value,
tree.columns.getNamedColumn("simtim-colDow"));
var dowFound = false;
var itemCount = mlistDow.itemCount;
for ( var i = 0; i < itemCount; i++ ) {
if ( mlistDow.getItemAtIndex(i).value === dow ) {
mlistDow.selectedIndex = i;
dowFound = true;
break;
}
}
if ( !dowFound ) {
alert(strbundle.getString("alert.error.invalid.dow"));
mlistDow.selectedIndex = 0;
}
else {
mlistDow.disabled = false;
}
}
else {
document.getElementById("simtim-cboxNotifyRecurring").checked = false;
mlistDow.disabled = true;
}
// Stash the row in a custom attribute.
document.getElementById("simtim-tboxNotifyDesc").
setAttribute("updaterow", row.value);
return;
}
if ( col.value.id === "simtim-colRecur" ) {
// Toggling Recurring also changes Day.
// Post-click value.
if ( tree.view.getCellValue(row.value, col.value) === "true" ) {
tree.view.setCellText(row.value,
tree.columns.getNamedColumn("simtim-colDow"),
strbundle.getString("display.dow.daily"));
tree.view.setCellValue(row.value,
tree.columns.getNamedColumn("simtim-colDow"), "7");
}
else {
tree.view.setCellText(row.value,
tree.columns.getNamedColumn("simtim-colDow"), "");
tree.view.setCellValue(row.value,
tree.columns.getNamedColumn("simtim-colDow"), "-1");
}
return;
}
if ( col.value.id === "simtim-colUrl" &&
event.button === 2 ) {
// User right-clicked on URL.
var url = tree.view.getCellText(row.value, col.value);
if ( url ) {
this.openLink(url);
}
}
}
},
// Called when Add/OK button is clicked.
onAddOkNotify: function() {
if ( this.onAddNotify() ) {
this.onOKNotify();
window.close();
}
},
// Called when Add button is clicked.
onAddNotify: function() {
// The timepicker element always stores its value as a 24 hr clock.
// For display, convert value to locale format, and remove seconds.
var strbundle = document.getElementById("simtim-strings");
var time24hr = document.getElementById("simtim-tspinNotifyTime").value;
var timeDisplay = new Date("December 23, 2007 " + time24hr).
toLocaleFormat("%X").replace(/:\d\d(?!.*:\d\d)/, "");
var tree = document.getElementById("simtim-treeNotifications");
var boxobject = tree.boxObject;
boxobject.QueryInterface(Components.interfaces.nsITreeBoxObject);
var treeChildren = document.getElementById("simtim-treeItems");
var treeItems, row = null;
var timeNode, dayNode = null;
var dowValue;
var descr = document.getElementById("simtim-tboxNotifyDesc").value;
var recurring = document.getElementById("simtim-cboxNotifyRecurring").checked;
var updaterow = document.getElementById('simtim-tboxNotifyDesc').
getAttribute("updaterow");
if ( updaterow ) {
tree.disabled = false;
// Updating a notification, enable all buttons.
document.getElementById("simtim-btnDel").disabled = false;
document.getElementById("simtim-btnDelAll").disabled = false;
document.getElementById("simtim-btnReset").disabled = false;
// Reset label and tooltip on Add and Add/OK buttons.
document.getElementById("simtim-btnAddOk").label =
strbundle.getString("button.label.add/OK");
document.getElementById("simtim-btnAddOk").tooltipText =
strbundle.getString("button.tooltip.add/OK");
document.getElementById("simtim-btnAdd").label =
strbundle.getString("button.label.add");
document.getElementById("simtim-btnAdd").tooltipText =
strbundle.getString("button.tooltip.add");
}
// If URL present, check for valid protocol.
var url = document.getElementById("simtim-tboxNotifyUrl").value;
if ( url ) {
// Strip whitespace.
newUrl = url.replace(/[\n\t ]+/, "");
if ( this.validateUrl(newUrl) ) {
document.getElementById("simtim-tboxNotifyUrl").value = newUrl;
url = newUrl;
}
else {
alert(strbundle.getString("alert.error.invalid.url"));
return false;
}
}
if ( recurring ) {
// Default dow is daily.
var mlistDow = document.getElementById("simtim-mlistDow");
var dowSelected = mlistDow.selectedItem;
dowValue = dowSelected ?
dowSelected.value : "7"; // daily = 7
}
else {
dowValue = "-1";
}
// Check if the new/updated notification time is a duplicate.
if ( treeChildren.hasChildNodes() ) {
var dup = false;
treeItems = treeChildren.childNodes;
tree.view.selection.clearSelection();
var len = treeItems.length;
for ( var i = 0; i < len; i++ ) {
row = treeItems[i].firstChild;
timeNode = row.firstChild;
dayNode = timeNode.nextSibling.nextSibling;
if ( timeDisplay === timeNode.getAttribute("label") &&
parseInt(updaterow, 10) !== i ) {
// Time match.
if ( dowValue === dayNode.getAttribute("value") ||
this.checkDuplicateDay(parseInt(dayNode.getAttribute("value"), 10),
parseInt(dowValue, 10))) {
// dow match or conflict.
// Maybe more than one dup, keep searching and highlight them all.
// Parm "true" indicates add to current selection.
tree.view.selection.rangedSelect(i, i, true);
dup = true;
}
}
}
if ( dup ) {
// Notify user the not. was added, but with conflict(s).
// Alerts will occur about 3 seconds apart.
updaterow ? alert(strbundle.getString("alert.warning.duplicate.notification.upd")) :
alert(strbundle.getString("alert.warning.duplicate.notification.add"));
}
}
var data = {completed: false, recurring: recurring, timeDisplay: timeDisplay,
description: descr, time24hr: time24hr, timeMilli: 0,
day: dowValue, url: url};
this.addTreeRow(treeChildren, data);
// Select the added/updated row. Add to any existing selections (duplicates).
var rowIndex;
if ( updaterow ) {
rowIndex = parseInt(updaterow, 10);
}
else if ( treeItems ) {
rowIndex = treeItems.length - 1;
}
else {
// Should never reach this.
rowIndex = 0;
}
tree.view.selection.rangedSelect(rowIndex, rowIndex, true);
// Ensure the added/updated row is visible, tree displays 5 rows (unless dlg stretched).
if ( treeItems && treeItems.length > 5 ) {
boxobject.ensureRowIsVisible(rowIndex);
}
// Clear the textboxes.
document.getElementById("simtim-tboxNotifyDesc").value = "";
document.getElementById("simtim-tboxNotifyUrl").value = "";
return true;
},
// Add a notification to the tree.
addTreeRow: function(treeChildren, data) {
var item = document.createElement("treeitem");
var row = document.createElement("treerow");
var cell;
// First cell is "timeDisplay", not editable. Stash "time24hr" in value.
cell = document.createElement("treecell");
cell.setAttribute("label", data.timeDisplay);
cell.setAttribute("value", data.time24hr);
cell.setAttribute("editable", "false");
row.appendChild(cell);
// Second cell is "recurring", displayed as a checkbox.
cell = document.createElement("treecell");
cell.setAttribute("value", data.recurring);
row.appendChild(cell);
// Third cell is "day". Stash numeric value of day in value.
cell = document.createElement("treecell");
cell.setAttribute("label", this.getDow(parseInt(data.day, 10)));
cell.setAttribute("value", data.day);
cell.setAttribute("editable", "false");
row.appendChild(cell);
// Fourth cell is "URL", the optional URL".
cell = document.createElement("treecell");
cell.setAttribute("label", data.url);
cell.setAttribute("editable", "false");
row.appendChild(cell);
// Fifth cell is "descr", the optional description. Stash "completed".
cell = document.createElement("treecell");
cell.setAttribute("label", data.description);
cell.setAttribute("value", data.completed);
cell.setAttribute("editable", "false");
row.appendChild(cell);
// Add the treerow onto treeitem.
item.appendChild(row);
// Append or replace? Check our custom attrribute.
var updaterow = document.getElementById("simtim-tboxNotifyDesc").
getAttribute("updaterow");
if ( updaterow ) {
treeChildren.replaceChild(item, treeChildren.childNodes[parseInt(updaterow, 10)]);
document.getElementById("simtim-tboxNotifyDesc").setAttribute("updaterow", "");
}
else {
treeChildren.appendChild(item);
}
},
// Called when Delete button is clicked.
onDeleteNotify: function() {
var strbundle = document.getElementById("simtim-strings");
var tree = document.getElementById("simtim-treeNotifications");
if ( tree.currentIndex < 0 ) {
// User didn't make a selection.
alert(strbundle.getString("alert.warning.no.item.selected"));
}
else {
var start = {};
var end = {};
var treeItem;
var numRanges = tree.view.selection.getRangeCount();
// Start with the last selection range and work up.
for ( var i = numRanges - 1; i >= 0; i-- ) {
tree.view.selection.getRangeAt(i,start,end);
// Within a range, delete from the bottom up.
for ( var j = end.value; j >= start.value; j-- ) {
treeItem = tree.view.getItemAtIndex(j);
treeItem.parentNode.removeChild(treeItem);
}
}
}
},
// Called when Del All button is clicked.
onDeleteAllNotify: function() {
var treeChildren = document.getElementById("simtim-treeItems");
// Remove from the bottom.
while ( treeChildren.hasChildNodes() ) {
treeChildren.removeChild(treeChildren.lastChild);
}
},
// Check if newly added/updated not. will result in alarms
// occuring at the same time.
checkDuplicateDay: function(oldDow, newDow) {
// Not. day values:
// 0-6 for Sun-Sat,
// 7 daily, 8 weekdays, 9 weekends.
// -1 for non-recurring (ignore them).
if ( oldDow === 7 || newDow === 7 ||
(( oldDow === 0 || oldDow === 6 ) && newDow === 9) ||
(( newDow === 0 || newDow === 6 ) && oldDow === 9) ||
(( oldDow > 0 && oldDow < 6 ) && newDow === 8) ||
(( newDow > 0 && newDow < 6 ) && oldDow === 8) ) {
return true;
}
else {
return false;
}
},
// Convert numeric value to day of week.
getDow: function(dow) {
var strbundle = document.getElementById("simtim-strings");
var daysOfWeek = [
strbundle.getString("display.dow.short.sunday"),
strbundle.getString("display.dow.short.monday"),
strbundle.getString("display.dow.short.tuesday"),
strbundle.getString("display.dow.short.wednesday"),
strbundle.getString("display.dow.short.thursday"),
strbundle.getString("display.dow.short.friday"),
strbundle.getString("display.dow.short.saturday"),
strbundle.getString("display.dow.daily"),
strbundle.getString("display.dow.short.weekdays"),
strbundle.getString("display.dow.short.weekend")
];
if ( daysOfWeek[dow] != null ) {
// When undefined (ie. daysOfWeek[-1]) is loosely compared to null, the result is true.
return daysOfWeek[dow];
}
else if ( dow === -1 ) {
// Non-recurring.
return "";
}
else {
alert(strbundle.getString("alert.error.invalid.dow"));
return "Err";
}
},
// Called when user adds a notification containing a URL.
validateUrl: function(url) {
// Check for supported protocols.
if ( url.match(/^http:\/\//) ||
url.match(/^https:\/\//) ||
url.match(/^file:\/\/\//) ||
url.match(/^ftp:\/\//) ||
url.match(/^chrome:\/\//) ) {
return true;
}
else {
return false;
}
},
// Convert notification time to milli.
calcNotTimeMilli: function(time24hr) {
var notTime = time24hr.split(":");
var currDate = new Date();
var day = currDate.getDate();
// Note the use of a radix in parseInt, otherwise
// leading 0 values get treated as octal. (eg "08" "09" return 0)
var notDate = new Date(currDate.getFullYear(), currDate.getMonth(),
day, parseInt(notTime[0], 10), parseInt(notTime[1], 10));
if ( currDate.getTime() >= notDate.getTime() ){
// Also adjusts for dst if necessary.
notDate.setDate(day + 1);
}
return notDate.getTime();
},
// Called when user clicks OK.
onOKNotify: function() {
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService).
getBranch("extensions.simpletimer@grbradt.org.");
this.buildNotifyTable();
this.updateNotifyPref();
// Trigger pref observer, which starts not. processing.
prefs.setBoolPref("notifyClosed", !prefs.getBoolPref("notifyClosed"));
},
// Construct an array of notification objects.
// Called when the user clicks OK or Add/OK.
buildNotifyTable: function() {
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
var treeChildren = document.getElementById("simtim-treeItems");
var notifyTable = [];
if ( treeChildren.hasChildNodes() ) {
// Build table of all nots. from dialog not. tree.
var row, timeNode, timeDisplay, time24hr, timeMilli, recurring, dayNode,
day, url, description, completed;
var treeItems = treeChildren.childNodes;
var len = treeItems.length;
for ( var i = 0; i < len; i++ ) {
row = treeItems[i].firstChild;
timeNode = row.firstChild;
timeDisplay = timeNode.getAttribute("label");
// Zero pad the hours if necessary.
time24hr = timeNode.getAttribute("value");
time24hr = ( time24hr.indexOf(":") === 1 ) ?
"0" + time24hr : time24hr;
timeMilli = this.calcNotTimeMilli(time24hr);
// Convert string attribute to boolean.
recurring = ( row.firstChild.nextSibling.
getAttribute("value") === "true" ) ?
true : false;
dayNode = row.firstChild.nextSibling.nextSibling;
day = parseInt(dayNode.getAttribute("value"), 10);
url = row.lastChild.previousSibling.getAttribute("label");
description = row.lastChild.getAttribute("label");
completed = ( row.lastChild.getAttribute("value") === "true" ) ?
true : false;
notifyTable[i] = {completed: completed,
recurring: recurring,
timeDisplay: timeDisplay,
description: description,
time24hr: time24hr,
timeMilli: timeMilli,
day: day,
url: url
};
}
}
// Sort the table on time24hr property.
if ( notifyTable.length > 1 ) {
notifyTable.sort(this.sortNotifyTable);
}
Application.storage.set("notifyTable", notifyTable);
},
// Construct an array of notification objects from the notifyRecur pref,
// and store via FUEL. Called at startup.
buildNotifyTableFromPref: function(notifyRecur) {
var strbundle = document.getElementById("simtim-strings");
// Objects stored as JSON strings in the pref.
var json = Components.classes["@mozilla.org/dom/json;1"].
createInstance(Components.interfaces.nsIJSON);
var notExpired = false;
var description;
var eventTime, eventDescription, eventUrl;
var expiredArray = [];
var notifyTable = [];
var notifyArray = notifyRecur.split(this.notifySep);
var currTimeMilli = new Date().getTime();
for ( var i in notifyArray ) {
notifyTable[i] = json.decode(notifyArray[i]);
// Check if a non-recurring not. expired while user
// was logged off, if so mark completed.
if ( !notifyTable[i].recurring &&
!notifyTable[i].completed &&
currTimeMilli > notifyTable[i].timeMilli ) {
notExpired = true;
notifyTable[i].completed = true;
expiredArray.push({timeDisplay: notifyTable[i].timeDisplay,
description: notifyTable[i].description});
if ( SimpleTimer.eventLogging ) {
eventTime = notifyTable[i].timeDisplay;
eventDescription = ( notifyTable[i].description ) ?
notifyTable[i].description :
strbundle.getString("msg.none");
eventUrl = ( notifyTable[i].url ) ?
notifyTable[i].url :
strbundle.getString("msg.none");
// Pass event type, event time, recurring, status, description, URL.
SimpleTimerEventLog.logEvent(strbundle.getString("msg.notification"),
eventTime,
strbundle.getString("msg.no"),
strbundle.getString("msg.expired"),
eventDescription,
eventUrl);
}
}
if ( !notifyTable[i].completed ) {
notifyTable[i].timeMilli = this.calcNotTimeMilli(notifyTable[i].time24hr);
}
}
// Sort the table on time24hr property.
if ( notifyTable.length > 1 ) {
notifyTable.sort(this.sortNotifyTable);
}
Application.storage.set("notifyTable", notifyTable);
if ( notExpired ) {
this.updateNotifyPref();
if ( !SimpleTimer.otherBrowserWindowOpen() ) {
var params = { displayItems: expiredArray, msg: strbundle.getString("msg.notify.lost")};
setTimeout(SimpleTimerSliderAlert.addMessageToQueue, 5000, params);
// setTimeout("SimpleTimer.displayPopup('lostNotify')", 5000);
setTimeout("SimpleTimer.playSound(3)", 5000);
}
}
},
// Sort the notification table on objects time24hr property.
sortNotifyTable: function(a, b) {
var x = a.time24hr;
var y = b.time24hr;
return ( (x < y) ? -1 : ( (x > y) ? 1 : 0 ) );
},
// Convert notifications to JSON strings and store in notifyRecur pref.
// The name "notifyRecur" is now a misnomer since all nots. are retained
// between sessions, it is a legacy from earlier versions where only
// recurring nots. were retained.
updateNotifyPref: function() {
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService).
getBranch("extensions.simpletimer@grbradt.org.");
// For Unicode.
var str = Components.classes["@mozilla.org/supports-string;1"].
createInstance(Components.interfaces.nsISupportsString);
var Application = Components.classes["@mozilla.org/fuel/application;1"].
getService(Components.interfaces.fuelIApplication);
var notifyTable = [];
if ( Application.storage.has("notifyTable") ) {
notifyTable = Application.storage.get("notifyTable", "ERR");
if ( notifyTable === "ERR" ) {
alert(strbundle.getString("alert.error.loading.notifications"));
return;
}
}
else {
alert(strbundle.getString("alert.error.loading.notifications"));
return;
}
// Objects stored as JSON strings in the pref.
var json = Components.classes["@mozilla.org/dom/json;1"].
createInstance(Components.interfaces.nsIJSON);
var prefString = "";
for ( var i in notifyTable ) {
prefString += json.encode(notifyTable[i]) + this.notifySep;
}
// Remove last separator.
if ( prefString ) {
prefString = prefString.substring(0, prefString.length - 1);
}
str.data = prefString;
prefs.setComplexValue("notifyRecur",
Components.interfaces.nsISupportsString, str);
},
// Open page in new tab.
// This function developed by Devon Jensen, from Download Statusbar.
openLink: function(aPage) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService();
var wmed = wm.QueryInterface(Components.interfaces.nsIWindowMediator);
var win = wmed.getMostRecentWindow("navigator:browser");
if ( !win ) {
win = window.openDialog(
"chrome://browser/content/browser.xul",
"_blank",
"chrome, all, dialog=no",
aPage, null, null);
}
else {
var content = win.document.getElementById("content");
content.selectedTab = content.addTab(aPage);
}
},
// Dump to console.
dumpNotificationObject: function (notification) {
this.debug("\nNotification data:");
this.debug("Completed?: " + notification.completed);
this.debug("Recurring?: " + notification.recurring);
this.debug("Notification time: " + notification.timeDisplay);
this.debug("Description: " + notification.description);
this.debug("Notification time (24 hr format): " + notification.time24hr);
this.debug("Notification time (millisecs): " + notification.timeMilli);
this.debug("Notification day: " + notification.day);
this.debug("Url: " + notification.url);
},
// Debug messages to console.
debug: function (aMsg) {
setTimeout(function() { throw new Error("[debug] " + aMsg); }, 0);
}
};